package com.google.fbyoutube.server;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.jsr107cache.Cache;
import net.sf.jsr107cache.CacheException;
import net.sf.jsr107cache.CacheFactory;
import net.sf.jsr107cache.CacheManager;

import com.google.apphosting.api.DeadlineExceededException;
import com.google.fbyoutube.shared.FeedUrlMaker;
import com.google.fbyoutube.shared.ytVideoEntry;
import com.google.gdata.data.youtube.VideoEntry;

/**
 * This class is designed to recache videods from YouTube, fetch videos by tab, page and time for all categories;
 * URL format:
 * http://gdata.youtube.com/feeds/api/standardfeeds/{most_popular:tab}?v=2&time={today:time}&max-results=20&start-index={61:page}
 * 
 * To run entire cache lifecircyle, this servlet need to be invoke 3*3*5 = 45 times, which takes 45 minutes;
 * That's to day, YouTube standard videos will be recache every 90 minutes.
 * 
 * @author Xiao Dao
 *
 */
public class CacheVideoFeedService extends HttpServlet {

	private static final long serialVersionUID = 3195868387839252212L;

	private static Logger logger = Logger.getLogger(CacheVideoFeedService.class.getName());

	private YouTubeAppServiceImpl videoService = null;
	
	private static Cache cache = null;
	
	private static int counter = 0;
	
	private static final String START_PREFIX = "&start-index=";
	private static final String TIME_PREFIX = "&time=";
	private static final String MAX_RESULT_PREFIX = "&max-results=";
	private static final String VERSION2 = "?v=2";
	
	private static ArrayList<String> feedUrls = new ArrayList<String>(20);
	
	private static final ArrayList<String> categories = new ArrayList<String>(FeedUrlMaker.categorySpec.keySet());
	
	private static final String[] pageIndex = new String[] {
		"1"//, "21", "41", "61", "81"
	};
	
	private static final ArrayList<String> tabs = new ArrayList<String>(FeedUrlMaker.typeSpec.values());
	
	private static final String[] timeIndex = new String[] {
		"today", "this_week", "this_month"
	};
	
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) {

		initCache();
		
		counter = Calendar.getInstance().get(Calendar.MINUTE);
		
		if (req.getParameter("counter") != null) {
			// page is the index of startIndex array, ranging from 0 ~ 4;
			counter = Integer.parseInt(req.getParameter("counter"));
		}
		
		// Generate start index of videos
		int page = counter % pageIndex.length;
		
		String start = pageIndex[page];
		
		// Generate tab of the videos
		constructFeedUrls();
		
		// Generate time of the videos
		int tm = (counter / (pageIndex.length * tabs.size()) ) % timeIndex.length;
		String wTime = timeIndex[tm];
		
		videoService = new YouTubeAppServiceImpl(req);
		for (String feedUrl_ : feedUrls) {
			
			String feedUrl = feedUrl_ + TIME_PREFIX + wTime + MAX_RESULT_PREFIX + 20 + START_PREFIX + start;
			
			List<VideoEntry> vodList = videoService.fetchVideos(feedUrl);
			List<ytVideoEntry> tempList = new ArrayList<ytVideoEntry>();
			if (vodList != null) {
				for (VideoEntry vod : vodList) {
					ytVideoEntry newVideo = videoService.convertVideoEntry(vod);
					tempList.add(newVideo);
				}
			} else {
				logger.warning("Failed to get feed from service: " + feedUrl);
			}
			
			if (tempList.size() > 0) {
				try {
					cache.put(feedUrl, tempList);
				} catch (DeadlineExceededException e) {
					logger.warning("DeadlineExceededException thrown while put video feed: " + feedUrl + " list size: " + tempList.size());
				}
//				logger.warning("put videos in feed cache, feedurl " + feedUrl + " size = " + tempList.size() + "at " + new SimpleDateFormat().format(new Date()));
				try {
					resp.getWriter().println("put videos in feed cache, feedurl " + feedUrl + " size = " + tempList.size() + "at " + new SimpleDateFormat().format(new Date()));
					resp.getWriter().print("<br />");
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
//		counter++;
		
		// Print out cache video keys
//		try {
//			resp.getWriter().print("<br />Counter : " + counter + " <br />");
//			resp.getWriter().print("Print Cache Videos<br />");
//			resp.getWriter().print(cache.size());
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
	}

	private void constructFeedUrls() {
		feedUrls.clear();
		int tabIndex = ( counter / pageIndex.length ) % tabs.size();
		String url = tabs.get(tabIndex) + VERSION2;
		feedUrls.add(url);
		
		for (int i = 0; i < categories.size(); ++i) {
			if (categories.get(i) != null) {
				url = tabs.get(tabIndex) + "_" + categories.get(i) + VERSION2;
				feedUrls.add(url);
			}
		}
	}

	private static void initCache() {
		if (cache == null) {
			try {
				CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
				cache = cacheFactory.createCache(Collections.emptyMap());
			} catch (CacheException e) {
				e.printStackTrace();
			}
		}
	}
	
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
		doGet(req, resp);
	}
	
	@SuppressWarnings("unchecked")
	public static List<ytVideoEntry> getCachedVideoFeed(String feedUrl) {
		if (cache == null) {
			return null;
		}
		
		Object o = cache.get(feedUrl);
		if (o != null) {
			return (List<ytVideoEntry>) o;
		} else {
			return null;
		}
		
	}
	
	public static void updateCache(String feedUrl, List<ytVideoEntry> list) {
		initCache();
		cache.put(feedUrl, list);
	}
	
}

